<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">


<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    
    <title>9. 类 &mdash; Python tutorial 3.4 documentation</title>
    
    <link rel="stylesheet" href="_static/default.css" type="text/css" />
    <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
    
    <script type="text/javascript">
      var DOCUMENTATION_OPTIONS = {
        URL_ROOT:    './',
        VERSION:     '3.4',
        COLLAPSE_INDEX: false,
        FILE_SUFFIX: '.html',
        HAS_SOURCE:  true
      };
    </script>
    <script type="text/javascript" src="_static/jquery.js"></script>
    <script type="text/javascript" src="_static/underscore.js"></script>
    <script type="text/javascript" src="_static/doctools.js"></script>
    <link rel="top" title="Python tutorial 3.4 documentation" href="index.html" />
    <link rel="next" title="10. Python 标准库概览" href="stdlib.html" />
    <link rel="prev" title="8. 错误和异常" href="errors.html" /> 
  </head>
  <body>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             accesskey="I">index</a></li>
        <li class="right" >
          <a href="stdlib.html" title="10. Python 标准库概览"
             accesskey="N">next</a> |</li>
        <li class="right" >
          <a href="errors.html" title="8. 错误和异常"
             accesskey="P">previous</a> |</li>
        <li><a href="index.html">Python tutorial 3.4 documentation</a> &raquo;</li> 
      </ul>
    </div>  

    <div class="document">
      <div class="documentwrapper">
        <div class="bodywrapper">
          <div class="body">
            
  <div class="section" id="tut-classes">
<span id="id1"></span><h1>9. 类<a class="headerlink" href="#tut-classes" title="Permalink to this headline">¶</a></h1>
<p>Python 的类机制通过最小的新语法和语义在语言中实现了类。 它是 C++ 或者 Modula-3 语言中类机制的混合。 就像模块一样，Python 的类并没有在用户和定义之间设立绝对的屏障，而是依赖于用户不去“强行闯入定义”的优雅。 另一方面，类的大多数重要特性都被完整的保留下来：类继承机制允许多重继承，派生类可以覆盖（override）基类中的任何方法或类，可以使用相同的方法名称调用基类的方法。 对象可以包含任意数量的私有数据。</p>
<p>用 C++ 术语来讲，所有的类成员（包括数据成员）都是公有（ <em>public</em> ）的，所有的成员函数都是虚 （ <em>virtual</em> ）的。用 Modula-3 的术语来讲，在成员方法中没有简便的方式引用对象的成员：方法函数在定义时需要以引用的对象做为第一个参数，调用时则会隐式引用对象。像在 Smalltalk 中一个，类也是对象。这就提供了导入和重命名语义。不像 C++ 和 Modula-3 中那样，大多数带有特殊语法的内置操作符（算法运算符、下标等）都可以针对类的需要重新定义。</p>
<p>（在讨论类时，没有足够的得到共识的术语，我会偶尔从 Smalltalk 和 C++ 借用一些。我比较喜欢用 Modula-3 的用语，因为比起 C++， Python 的面向对象语法更像它，但是我想很少有读者听过这个。）</p>
<div class="section" id="tut-object">
<span id="id2"></span><h2>9.1. 术语相关<a class="headerlink" href="#tut-object" title="Permalink to this headline">¶</a></h2>
<p>对象具有特性，并且多个名称（在多个作用于中）可以绑定在同一个对象上。 这在其它语言中被称为别名。 在对 Python 的第一印象中这通常会被忽略，并且当处理不可变基础类型（数字，字符串，元组）时可以被放心的忽略。 但是，在调用列表、字典这类可变对象，或者大多数程序外部类型（文件，窗体等）描述实体时，别名对 Python 代码的语义便具有（有意而为！）影响。 这通常有助于程序的优化，因为在某些方面别名表现的就像是指针。 例如，你可以轻易的传递一个对象，因为通过继承只是传递一个指针。 并且如果一个方法修改了一个作为参数传递的对象，调用者可以接收这一变化——这消除了两种不同的参数传递机制的需要，像 Pascal 语言。</p>
</div>
<div class="section" id="python">
<span id="tut-scopes"></span><h2>9.2. Python 作用域和命名空间<a class="headerlink" href="#python" title="Permalink to this headline">¶</a></h2>
<p>在介绍类之前，我首先介绍一些有关 Python 作用域的规则。类的定义非常巧妙的运用了命名空间，要完全理解接下来的知识，需要先理解作用域和命名空间的工作原理。另外，这一切的知识对于任何高级 Python 程序员都非常有用。</p>
<p>让我们从一些定义说起。</p>
<p><em>命名空间</em> 是从命名到对象的映射。当前命名空间主要是通过 Python 字典实现的，不过通常不关心具体的实现方式（除非出于性能考虑），以后也有可能会改变其实现方式。以下有一些命名空间的例子：内置命名（像 <tt class="xref py py-func docutils literal"><span class="pre">abs()</span></tt> 这样的函数，以及内置异常名）集，模块中的全局命名，函数调用中的局部命名。某种意义上讲对象的属性集也是一个命名空间。关于命名空间需要了解的一件很重要的事就是不同命名空间中的命名没有任何联系，例如两个不同的模块可能都会定义一个名为 <tt class="docutils literal"><span class="pre">maximize</span></tt> 的函数而不会发生混淆－－用户必须以模块名为前缀来引用它们。</p>
<p>顺便提一句，我称 Python 中任何一个“.”之后的命名为 <em>属性</em> －－例如，表达式 <tt class="docutils literal"><span class="pre">z.real</span></tt> 中的 <tt class="docutils literal"><span class="pre">real</span></tt> 是对象 <tt class="docutils literal"><span class="pre">z</span></tt> 的一个属性。严格来讲，从模块中引用命名是引用属性：表达式 <tt class="docutils literal"><span class="pre">modname.funcname</span></tt> 中， <tt class="docutils literal"><span class="pre">modname</span></tt> 是一个模块对象，<tt class="docutils literal"><span class="pre">funcname</span></tt> 是它的一个属性。因此，模块的属性和模块中的全局命名有直接的映射关系：它们共享同一命名空间！<a class="footnote-reference" href="#id19" id="id3">[1]</a></p>
<p>属性可以是只读过或写的。后一种情况下，可以对属性赋值。你可以这样作： <tt class="docutils literal"><span class="pre">modname.the_answer</span> <span class="pre">=</span> <span class="pre">42</span></tt> 。可写的属性也可以用 <tt class="xref std std-keyword docutils literal"><span class="pre">del</span></tt> 语句删除。例如： <tt class="docutils literal"><span class="pre">del</span> <span class="pre">modname.the_answer</span></tt> 会从 <tt class="docutils literal"><span class="pre">modname</span></tt> 对象中删除 <tt class="docutils literal"><span class="pre">the_answer</span></tt> 属性。</p>
<p>不同的命名空间在不同的时刻创建，有不同的生存期。包含内置命名的命名空间在 Python 解释器启动时创建，会一直保留，不被删除。模块的全局命名空间在模块定义被读入时创建，通常，模块命名空间也会一直保存到解释器退出。由解释器在最高层调用执行的语句，不管它是从脚本文件中读入还是来自交互式输入，都是 <tt class="xref py py-mod docutils literal"><span class="pre">__main__</span></tt> 模块的一部分，所以它们也拥有自己的命名空间。（内置命名也同样被包含在一个模块中，它被称作 <tt class="xref py py-mod docutils literal"><span class="pre">__builtin__</span></tt> 。）</p>
<p>当调用函数时，就会为它创建一个局部命名空间，并且在函数返回或抛出一个并没有在函数内部处理的异常时被删除。 （实际上，用遗忘来形容到底发生了什么更为贴切。） 当然，每个递归调用都有自己的局部命名空间。</p>
<p><em>作用域</em> 就是一个 Python 程序可以直接访问命名空间的正文区域。 这里的 直接访问 意思是一个对名称的错误引用会尝试在命名空间内查找。</p>
<p>尽管作用域是静态定义，在使用时他们都是动态的。每次执行时，至少有三个命名空间可以直接访问的作用域嵌套在一起：</p>
<ul class="simple">
<li>包含局部命名的使用域在最里面，首先被搜索；其次搜索的是中层的作用域，这里包含了同级的函数；最后搜索最外面的作用域，它包含内置命名。</li>
<li>首先搜索最内层的作用域，它包含局部命名任意函数包含的作用域，是内层嵌套作用域搜索起点，包含非局部，但是也非全局的命名</li>
<li>接下来的作用域包含当前模块的全局命名</li>
<li>最外层的作用域（最后搜索）是包含内置命名的命名空间。</li>
</ul>
<p>如果一个命名声明为全局的，那么所有的赋值和引用都直接针对包含模全局命名的中级作用域。另外，从外部访问到的所有内层作用域的变量都是只读的。（试图写这样的变量只会在内部作用域创建一个 <em>新</em> 局部变量，外部标示命名的那个变量不会改变）。</p>
<p>通常，局部作用域引用当前函数的命名。在函数之外，局部作用域与全局使用域引用同一命名空间：模块命名空间。类定义也是局部作用域中的另一个命名空间。</p>
<p>重要的是作用域决定于源程序的意义：一个定义于某模块中的函数的全局作用域是该模块的命名空间，而不是该函数的别名被定义或调用的位置，了解这一点非常重要。另一方面，命名的实际搜索过程是动态的，在运行时确定的——然而，Python 语言也在不断发展，以后有可能会成为静态的“编译”时确定，所以不要依赖动态解析！（事实上，局部变量已经是静态确定了。）</p>
<p>Python 的一个特别之处在于——如果没有使用 <tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 语法——其赋值操作总是在最里层的作用域。赋值不会复制数据——只是将命名绑定到对象。删除也是如此： <tt class="docutils literal"><span class="pre">del</span> <span class="pre">x</span></tt> 只是从局部作用域的命名空间中删除命名 <tt class="docutils literal"><span class="pre">x</span></tt> 。事实上，所有引入新命名的操作都作用于局部作用域。特别是 <tt class="xref std std-keyword docutils literal"><span class="pre">import</span></tt> 语句和函数定将模块名或函数绑定于局部作用域。（可以使用 <tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 语句将变量引入到全局作用域。）</p>
<p><tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 语句用以指明某个特定的变量为全局作用域，并重新绑定它。 <tt class="xref std std-keyword docutils literal"><span class="pre">nonlocal</span></tt> 语句用以指明某个特定的变量为封闭作用域，并重新绑定它。</p>
<div class="section" id="tut-scopeexample">
<span id="id4"></span><h3>9.2.1. 作用域和命名空间示例<a class="headerlink" href="#tut-scopeexample" title="Permalink to this headline">¶</a></h3>
<p>以下是一个示例，演示了如何引用不同作用域和命名空间，以及 <tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 和 <tt class="xref std std-keyword docutils literal"><span class="pre">nonlocal</span></tt> 如何影响变量绑定:</p>
<div class="highlight-python"><div class="highlight"><pre>def scope_test():
    def do_local():
        spam = &quot;local spam&quot;
    def do_nonlocal():
        nonlocal spam
        spam = &quot;nonlocal spam&quot;
    def do_global():
        global spam
        spam = &quot;global spam&quot;

    spam = &quot;test spam&quot;
    do_local()
    print(&quot;After local assignment:&quot;, spam)
    do_nonlocal()
    print(&quot;After nonlocal assignment:&quot;, spam)
    do_global()
    print(&quot;After global assignment:&quot;, spam)

scope_test()
print(&quot;In global scope:&quot;, spam)
</pre></div>
</div>
<p>以上示例代码的输出为:</p>
<div class="highlight-none"><div class="highlight"><pre>After local assignment: test spam
After nonlocal assignment: nonlocal spam
After global assignment: nonlocal spam
In global scope: global spam
</pre></div>
</div>
<p>注意： <em>local</em> 赋值语句是无法改变 <em>scope_test</em> 的 <em>spam</em> 绑定。 <tt class="xref std std-keyword docutils literal"><span class="pre">nonlocal</span></tt> 赋值语句改变了 <em>scope_test</em> 的 <em>spam</em> 绑定，并且 <tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 赋值语句从模块级改变了 spam 绑定。</p>
<p>你也可以看到在 <tt class="xref std std-keyword docutils literal"><span class="pre">global</span></tt> 赋值语句之前对 spam 是没有预先绑定的。</p>
</div>
</div>
<div class="section" id="tut-firstclasses">
<span id="id5"></span><h2>9.3. 初识类<a class="headerlink" href="#tut-firstclasses" title="Permalink to this headline">¶</a></h2>
<p>类引入了一些新语法：三种新的对象类型和一些新的语义。</p>
<div class="section" id="tut-classdefinition">
<span id="id6"></span><h3>9.3.1. 类定义语法<a class="headerlink" href="#tut-classdefinition" title="Permalink to this headline">¶</a></h3>
<p>类定义最简单的形式如下:</p>
<div class="highlight-python"><div class="highlight"><pre>class ClassName:
    &lt;statement-1&gt;
    .
    .
    .
    &lt;statement-N&gt;
</pre></div>
</div>
<p>类的定义就像函数定义（ <tt class="xref std std-keyword docutils literal"><span class="pre">def</span></tt> 语句），要先执行才能生效。（你当然可以把它放进 <tt class="xref std std-keyword docutils literal"><span class="pre">if</span></tt> 语句的某一分支，或者一个函数的内部。）</p>
<p>习惯上，类定义语句的内容通常是函数定义，不过其它语句也可以，有时会很有用——后面我们再回过头来讨论。类中的函数定义通常包括了一个特殊形式的参数列表，用于方法调用约定——同样我们在后面讨论这些。</p>
<p>进入类定义部分后，会创建出一个新的命名空间，作为局部作用域——因此，所有的赋值成为这个新命名空间的局部变量。特别是函数定义在此绑定了新的命名。</p>
<p>类定义完成时（正常退出），就创建了一个 <em>类对象</em> 。基本上它是对类定义创建的命名空间进行了一个包装；我们在下一节进一步学习类对象的知识。原始的局部作用域（类定义引入之前生效的那个）得到恢复，类对象在这里绑定到类定义头部的类名（例子中是 <tt class="xref py py-class docutils literal"><span class="pre">ClassName</span></tt> ）。</p>
</div>
<div class="section" id="tut-classobjects">
<span id="id7"></span><h3>9.3.2. 类对象<a class="headerlink" href="#tut-classobjects" title="Permalink to this headline">¶</a></h3>
<p>类对象支持两种操作：属性引用和实例化。</p>
<p><em>属性引用</em> 使用和 Python 中所有的属性引用一样的标准语法：<tt class="docutils literal"><span class="pre">obj.name</span></tt> 。类对象创建后，类命名空间中所有的命名都是有效属性名。所以如果类定义是这样:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">MyClass</span><span class="p">:</span>
    <span class="sd">&quot;&quot;&quot;A simple example class&quot;&quot;&quot;</span>
    <span class="n">i</span> <span class="o">=</span> <span class="mi">12345</span>
    <span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;hello world&#39;</span>
</pre></div>
</div>
<p>那么 <tt class="docutils literal"><span class="pre">MyClass.i</span></tt> 和 <tt class="docutils literal"><span class="pre">MyClass.f</span></tt> 是有效的属性引用，分别返回一个整数和一个方法对象。也可以对类属性赋值，你可以通过给 <tt class="docutils literal"><span class="pre">MyClass.i</span></tt> 赋值来修改它。 <tt class="xref py py-attr docutils literal"><span class="pre">__doc__</span></tt> 也是一个有效的属性，返回类的文档字符串： <tt class="docutils literal"><span class="pre">&quot;A</span> <span class="pre">simple</span> <span class="pre">example</span> <span class="pre">class&quot;</span></tt> 。</p>
<p>类的 <em>实例化</em> 使用函数符号。只要将类对象看作是一个返回新的类实例的无参数函数即可。例如（假设沿用前面的类）:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">x</span> <span class="o">=</span> <span class="n">MyClass</span><span class="p">()</span>
</pre></div>
</div>
<p>以上创建了一个新的类 <em>实例</em> 并将该对象赋给局部变量 <tt class="docutils literal"><span class="pre">x</span></tt> 。</p>
<p>这个实例化操作（“调用”一个类对象）来创建一个空的对象。很多类都倾向于将对象创建为有初始状态的。因此类可能会定义一个名为 <tt class="xref py py-meth docutils literal"><span class="pre">__init__()</span></tt> 的特殊方法，像下面这样:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
    <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
</pre></div>
</div>
<p>类定义了 <tt class="xref py py-meth docutils literal"><span class="pre">__init__()</span></tt> 方法的话，类的实例化操作会自动为新创建的类实例调用 <tt class="xref py py-meth docutils literal"><span class="pre">__init__()</span></tt> 方法。所以在下例中，可以这样创建一个新的实例:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">x</span> <span class="o">=</span> <span class="n">MyClass</span><span class="p">()</span>
</pre></div>
</div>
<p>当然，出于弹性的需要， <tt class="xref py py-meth docutils literal"><span class="pre">__init__()</span></tt> 方法可以有参数。事实上，参数通过 <tt class="xref py py-meth docutils literal"><span class="pre">__init__()</span></tt> 传递到类的实例化操作上。例如，</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">class</span> <span class="nc">Complex</span><span class="p">:</span>
<span class="gp">... </span>    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">realpart</span><span class="p">,</span> <span class="n">imagpart</span><span class="p">):</span>
<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">r</span> <span class="o">=</span> <span class="n">realpart</span>
<span class="gp">... </span>        <span class="bp">self</span><span class="o">.</span><span class="n">i</span> <span class="o">=</span> <span class="n">imagpart</span>
<span class="gp">...</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span> <span class="o">=</span> <span class="n">Complex</span><span class="p">(</span><span class="mf">3.0</span><span class="p">,</span> <span class="o">-</span><span class="mf">4.5</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">x</span><span class="o">.</span><span class="n">r</span><span class="p">,</span> <span class="n">x</span><span class="o">.</span><span class="n">i</span>
<span class="go">(3.0, -4.5)</span>
</pre></div>
</div>
</div>
<div class="section" id="tut-instanceobjects">
<span id="id8"></span><h3>9.3.3. 实例对象<a class="headerlink" href="#tut-instanceobjects" title="Permalink to this headline">¶</a></h3>
<p>现在我们可以用实例对象作什么？实例对象唯一可用的操作就是属性引用。有两种有效的属性名。</p>
<p><em>数据属性</em> 相当于 Smalltalk 中的“实例变量”或 C++ 中的“数据成员”。和局部变量一样，数据属性不需要声明，第一次使用时它们就会生成。例如，如果 <tt class="docutils literal"><span class="pre">x</span></tt> 是前面创建的 <tt class="xref py py-class docutils literal"><span class="pre">MyClass</span></tt> 实例，下面这段代码会打印出 16 而在堆栈中留下多余的东西:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">x</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">while</span> <span class="n">x</span><span class="o">.</span><span class="n">counter</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">:</span>
    <span class="n">x</span><span class="o">.</span><span class="n">counter</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="n">counter</span> <span class="o">*</span> <span class="mi">2</span>
<span class="k">print</span><span class="p">(</span><span class="n">x</span><span class="o">.</span><span class="n">counter</span><span class="p">)</span>
<span class="k">del</span> <span class="n">x</span><span class="o">.</span><span class="n">counter</span>
</pre></div>
</div>
<p>另一种为实例对象所接受的引用属性是 <em>方法</em> 。方法是“属于”一个对象的函数。（在 Python 中，方法不止是类实例所独有：其它类型的对象也可有方法。例如，链表对象有 append，insert，remove，sort 等等方法。然而，在后面的介绍中，除非特别说明，我们提到的方法特指类方法）</p>
<p id="index-0">实例对象的有效名称依赖于它的类。按照定义，类中所有（用户定义）的函数对象对应它的实例中的方法。所以在我们的例子中，<tt class="docutils literal"><span class="pre">x.f</span></tt> 是一个有效的方法引用，因为 <tt class="docutils literal"><span class="pre">MyClass.f</span></tt> 是一个函数。但 <tt class="docutils literal"><span class="pre">x.i</span></tt> 不是，因为 <tt class="docutils literal"><span class="pre">MyClass.i</span></tt> 不是函数。不过 <tt class="docutils literal"><span class="pre">x.f</span></tt> 和 <tt class="docutils literal"><span class="pre">MyClass.f</span></tt> 不同－－它是一个 <em>方法对象</em> ，不是一个函数对象。</p>
</div>
<div class="section" id="tut-methodobjects">
<span id="id9"></span><h3>9.3.4. 方法对象<a class="headerlink" href="#tut-methodobjects" title="Permalink to this headline">¶</a></h3>
<p>通常，方法通过右绑定方式调用:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">x</span><span class="o">.</span><span class="n">f</span><span class="p">()</span>
</pre></div>
</div>
<p>在 <tt class="xref py py-class docutils literal"><span class="pre">MyClass</span></tt> 示例中，这会返回字符串 <tt class="docutils literal"><span class="pre">'hello</span> <span class="pre">world'</span></tt> 。然而，也不是一定要直接调用方法。 <tt class="docutils literal"><span class="pre">x.f</span></tt> 是一个方法对象，它可以存储起来以后调用。例如:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="n">xf</span> <span class="o">=</span> <span class="n">x</span><span class="o">.</span><span class="n">f</span>
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">xf</span><span class="p">())</span>
</pre></div>
</div>
<p>会不断的打印 <tt class="docutils literal"><span class="pre">hello</span> <span class="pre">world</span></tt> 。</p>
<p>调用方法时发生了什么？你可能注意到调用 <tt class="docutils literal"><span class="pre">x.f()</span></tt> 时没有引用前面标出的变量，尽管在 <tt class="xref py py-meth docutils literal"><span class="pre">f()</span></tt> 的函数定义中指明了一个参数。这个参数怎么了？事实上如果函数调用中缺少参数，Python 会抛出异常－－甚至这个参数实际上没什么用……</p>
<p>实际上，你可能已经猜到了答案：方法的特别之处在于实例对象作为函数的第一个参数传给了函数。在我们的例子中，调用 <tt class="docutils literal"><span class="pre">x.f()</span></tt> 相当于 <tt class="docutils literal"><span class="pre">MyClass.f(x)</span></tt> 。通常，以 <em>n</em> 个参数的列表去调用一个方法就相当于将方法的对象插入到参数列表的最前面后，以这个列表去调用相应的函数。</p>
<p>如果你还是不理解方法的工作原理，了解一下它的实现也许有帮助。引用非数据属性的实例属性时，会搜索它的类。 如果这个命名确认为一个有效的函数对象类属性，就会将实例对象和函数对象封装进一个抽象对象：这就是方法对象。以一个参数列表调用方法对象时，它被重新拆 封，用实例对象和原始的参数列表构造一个新的参数列表，然后函数对象调用这个新的参数列表。</p>
</div>
</div>
<div class="section" id="tut-remarks">
<span id="id10"></span><h2>9.4. 一些说明<a class="headerlink" href="#tut-remarks" title="Permalink to this headline">¶</a></h2>
<p>数据属性会覆盖同名的方法属性。 为了避免意外的名称冲突，这在大型程序中是极难发现的 Bug，使用一些约定来减少冲突的机会是明智的。 可能的约定包括：大写方法名称的首字母，使用一个唯一的小字符串（也许只是一个下划线）作为数据属性名称的前缀，或者方法使用动词而数据属性使用名词。</p>
<p>数据属性可以被方法引用，也可以由一个对象的普通用户（客户）使用。 换句话说，类不能用来实现纯净的数据类型。 事实上，Python 中不可能强制隐藏数据——一切基于约定。 （如果需要，使用 C 编写的 Python 实现可以完全隐藏实现细节并控制对象的访问。这可以用来通过 C 语言扩展 Python。）</p>
<p>客户应该谨慎的使用数据属性——客户可能通过践踏他们的数据属性而使那些由方法维护的常量变得混乱。 注意：只要能避免冲突，客户可以向一个实例对象添加他们自己的数据属性，而不会影响方法的正确性——再次强调，命名约定可以避免很多麻烦。</p>
<p>从方法内部引用数据属性（或其他方法）并没有快捷方式。 我觉得这实际上增加了方法的可读性：当浏览一个方法时，在局部变量和实例变量之间不会出现令人费解的情况。</p>
<p>一般，方法的第一个参数被命名为 self 。 这仅仅是一个约定：对 Python 而言，名称 self 绝对没有任何特殊含义。 （但是请注意：如果不遵循这个约定，对其他的 Python 程序员而言你的代码可读性就会变差，而且有些 类查看器 程序也可能是遵循此约定编写的。）</p>
<p>类属性的任何函数对象都为那个类的实例定义了一个方法。 函数定义代码不一定非得定义在类中：也可以将一个函数对象赋值给类中的一个局部变量。 例如:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="c"># Function defined outside the class</span>
<span class="k">def</span> <span class="nf">f1</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
    <span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="p">)</span>

<span class="k">class</span> <span class="nc">C</span><span class="p">:</span>
    <span class="n">f</span> <span class="o">=</span> <span class="n">f1</span>
    <span class="k">def</span> <span class="nf">g</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="s">&#39;hello world&#39;</span>
    <span class="n">h</span> <span class="o">=</span> <span class="n">g</span>
</pre></div>
</div>
<p>现在 <tt class="docutils literal"><span class="pre">f</span></tt>, <tt class="docutils literal"><span class="pre">g</span></tt> 和 <tt class="docutils literal"><span class="pre">h</span></tt> 都是类 <tt class="xref py py-class docutils literal"><span class="pre">C</span></tt> 的属性，引用的都是函数对象，因此它们都是 <tt class="xref py py-class docutils literal"><span class="pre">C</span></tt> 实例的方法－－ <tt class="docutils literal"><span class="pre">h</span></tt> 严格等于 <tt class="docutils literal"><span class="pre">g</span></tt> 。要注意的是这种习惯通常只会迷惑程序的读者。</p>
<p>通过 <tt class="docutils literal"><span class="pre">self</span></tt> 参数的方法属性，方法可以调用其它的方法:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Bag</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">addtwice</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="n">x</span><span class="p">)</span>
</pre></div>
</div>
<p>方法可以像引用普通的函数那样引用全局命名。与方法关联的全局作用域是包含类定义的模块。（类本身永远不会做为全局作用域使用。）尽管很少有好的理由在方法 中使用全局数据，全局作用域确有很多合法的用途：其一是方法可以调用导入全局作用域的函数和方法，也可以调用定义在其中的类和函数。通常，包含此方法的类也会定义在这个全局作用域，在下一节我们会了解为何一个方法要引用自己的类。</p>
<p>每个值都是一个对象，因此每个值都有一个 类( <em>class</em> ) （也称为它的 类型( <em>type</em> ) ），它存储为 <tt class="docutils literal"><span class="pre">object.__class__</span></tt> 。</p>
</div>
<div class="section" id="tut-inheritance">
<span id="id11"></span><h2>9.5. 继承<a class="headerlink" href="#tut-inheritance" title="Permalink to this headline">¶</a></h2>
<p>当然，如果一种语言不支持继承就，“类”就没有什么意义。派生类的定义如下所示:</p>
<div class="highlight-python"><div class="highlight"><pre>class DerivedClassName(BaseClassName):
    &lt;statement-1&gt;
    .
    .
    .
    &lt;statement-N&gt;
</pre></div>
</div>
<p>命名 <tt class="xref py py-class docutils literal"><span class="pre">BaseClassName</span></tt> （示例中的基类名）必须与派生类定义在一个作用域内。除了类，还可以用表达式，基类定义在另一个模块中时这一点非常有用:</p>
<div class="highlight-python"><div class="highlight"><pre>class DerivedClassName(modname.BaseClassName):
</pre></div>
</div>
<p>派生类定义的执行过程和基类是一样的。构造派生类对象时，就记住了基类。这在解析属性引用的时候尤其有用：如果在类中找不到请求调用的属性，就搜索基类。如果基类是由别的类派生而来，这个规则会递归的应用上去。</p>
<p>派生类的实例化没有什么特殊之处： <tt class="docutils literal"><span class="pre">DerivedClassName()</span></tt> （示列中的派生类）创建一个新的类实例。方法引用按如下规则解析：搜索对应的类属性，必要时沿基类链逐级搜索，如果找到了函数对象这个方法引用就是合法的。</p>
<p>派生类可能会覆盖其基类的方法。因为方法调用同一个对象中的其它方法时没有特权，基类的方法调用同一个基类的方法时，可能实际上最终调用了派生类中的覆盖方法。（对于 C++ 程序员来说，Python 中的所有方法本质上都是 <tt class="docutils literal"><span class="pre">虚</span></tt> 方法。）</p>
<p>派生类中的覆盖方法可能是想要扩充而不是简单的替代基类中的重名方法。有一个简单的方法可以直接调用基类方法，只要调用： <tt class="docutils literal"><span class="pre">BaseClassName.methodname(self,</span> <span class="pre">arguments)</span></tt>。有时这对于客户也很有用。（要注意只有 <tt class="docutils literal"><span class="pre">BaseClassName</span></tt>  在同一全局作用域定义或导入时才能这样用。）</p>
<p>Python 有两个用于继承的函数：</p>
<ul class="simple">
<li>函数 <tt class="xref py py-func docutils literal"><span class="pre">isinstance()</span></tt> 用于检查实例类型： <tt class="docutils literal"><span class="pre">isinstance(obj,</span> <span class="pre">int)</span></tt> 只有在 <tt class="docutils literal"><span class="pre">obj.__class__</span></tt> 是 <tt class="xref py py-class docutils literal"><span class="pre">int</span></tt> 或其它从 <tt class="xref py py-class docutils literal"><span class="pre">int</span></tt> 继承的类型</li>
<li>函数 <tt class="xref py py-func docutils literal"><span class="pre">issubclass()</span></tt> 用于检查类继承： <tt class="docutils literal"><span class="pre">issubclass(bool,</span> <span class="pre">int)</span></tt> 为 <tt class="docutils literal"><span class="pre">True</span></tt> ，因为 <tt class="xref py py-class docutils literal"><span class="pre">bool</span></tt> 是 int 的子类。但是， <tt class="docutils literal"><span class="pre">issubclass(unicode,</span> <span class="pre">str)</span></tt> 是 <tt class="docutils literal"><span class="pre">False</span></tt> ，因为 <tt class="xref py py-class docutils literal"><span class="pre">unicode</span></tt> 不是 <tt class="xref py py-class docutils literal"><span class="pre">str</span></tt> 的子类（它们只是共享一个通用祖先类 <tt class="xref py py-class docutils literal"><span class="pre">basestring</span></tt> ）。</li>
</ul>
<div class="section" id="tut-multiple">
<span id="id12"></span><h3>9.5.1. 多继承<a class="headerlink" href="#tut-multiple" title="Permalink to this headline">¶</a></h3>
<p>Python 同样有限的支持多继承形式。多继承的类定义形如下例:</p>
<div class="highlight-python"><div class="highlight"><pre>class DerivedClassName(Base1, Base2, Base3):
    &lt;statement-1&gt;
    .
    .
    .
    &lt;statement-N&gt;
</pre></div>
</div>
<p>在大多数情况下，在最简单的情况下，你能想到的搜索属性从父类继承的深度优先，左到右，而不是搜索两次在同一个类层次结构中，其中有一个重叠。因此，如果在 <tt class="xref py py-class docutils literal"><span class="pre">DerivedClassName</span></tt> （示例中的派生类）中没有找到某个属性，就会搜索 <tt class="xref py py-class docutils literal"><span class="pre">Base1</span></tt> ，然后（递归的）搜索其基类，如果最终没有找到，就搜索 <tt class="xref py py-class docutils literal"><span class="pre">Base2</span></tt> ，以此类推。</p>
<p>实际上，<tt class="xref py py-func docutils literal"><span class="pre">super()</span></tt> 可以动态的改变解析顺序。这个方式可见于其它的一些多继承语言，类似 call-next-method，比单继承语言中的 super 更强大 。</p>
<p>动态调整顺序十分必要的，因为所有的多继承会有一到多个菱形关系（指有至少一个祖先类可以从子类经由多个继承路径到达）。例如，所有的 new-style 类继承自 <tt class="xref py py-class docutils literal"><span class="pre">object</span></tt> ，所以任意的多继承总是会有多于一条继承路径到达 <tt class="xref py py-class docutils literal"><span class="pre">object</span></tt> 。为了防止重复访问基类，通过动态的线性化算法，每个类都按从左到右的顺序特别指定了顺序，每个祖先类只调用一次，这是单调的（意味着一个类被继承时不会影响它祖先的次序）。总算可以通过这种方式使得设计一个可靠并且可扩展的多继承类成为可能。进一步的内容请参见 <a class="reference external" href="http://www.python.org/download/releases/2.3/mro/">http://www.python.org/download/releases/2.3/mro/</a> 。</p>
</div>
</div>
<div class="section" id="tut-private">
<span id="id13"></span><h2>9.6. 私有变量<a class="headerlink" href="#tut-private" title="Permalink to this headline">¶</a></h2>
<p>只能从对像内部访问的“私有”实例变量，在 Python 中不存在。然而，也有一个变通的访问用于大多数 Python 代码：以一个下划线开头的命名（例如 <tt class="docutils literal"><span class="pre">_spam</span></tt> ）会被处理为 API 的非公开部分（无论它是一个函数、方法或数据成员）。它会被视为一个实现细节，无需公开。</p>
<p>因为有一个正当的类私有成员用途（即避免子类里定义的命名与之冲突），Python 提供了对这种结构的有限支持，称为 <em class="dfn">name mangling</em> （命名编码） 。任何形如 <tt class="docutils literal"><span class="pre">__spam</span></tt> 的标识（前面至少两个下划线，后面至多一个），被替代为 <tt class="docutils literal"><span class="pre">_classname__spam</span></tt> ，去掉前导下划线的 <tt class="docutils literal"><span class="pre">classname</span></tt> 即当前的类名。此语法不关注标识的位置，只要求在类定义内。</p>
<p>名称重整是有助于子类重写方法，而不会打破组内的方法调用。 例如:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Mapping</span><span class="p">:</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">iterable</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">items_list</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">__update</span><span class="p">(</span><span class="n">iterable</span><span class="p">)</span>

    <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">iterable</span><span class="p">):</span>
        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">iterable</span><span class="p">:</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">items_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>

    <span class="n">__update</span> <span class="o">=</span> <span class="n">update</span>   <span class="c"># private copy of original update() method</span>

<span class="k">class</span> <span class="nc">MappingSubclass</span><span class="p">(</span><span class="n">Mapping</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">update</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">keys</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
        <span class="c"># provides new signature for update()</span>
        <span class="c"># but does not break __init__()</span>
        <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">keys</span><span class="p">,</span> <span class="n">values</span><span class="p">):</span>
            <span class="bp">self</span><span class="o">.</span><span class="n">items_list</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">item</span><span class="p">)</span>
</pre></div>
</div>
<p>需要注意的是编码规则设计为尽可能的避免冲突，被认作为私有的变量仍然有可能被访问或修改。在特定的场合它也是有用的，比如调试的时候。</p>
<p>要注意的是代码传入 <tt class="docutils literal"><span class="pre">exec</span></tt> ， <tt class="docutils literal"><span class="pre">eval()</span></tt> 或 <tt class="docutils literal"><span class="pre">execfile()</span></tt> 时不考虑所调用的类的类名，视其为当前类，这类似于 <tt class="docutils literal"><span class="pre">global</span></tt> 语句的效应，已经按字节编译的部分也有同样的限制。这也同样作用于 <tt class="docutils literal"><span class="pre">getattr()</span></tt> ， <tt class="docutils literal"><span class="pre">setattr()</span></tt> 和 <tt class="docutils literal"><span class="pre">delattr()</span></tt> ，像直接引用 <tt class="docutils literal"><span class="pre">__dict__</span></tt> 一样。</p>
</div>
<div class="section" id="tut-odds">
<span id="id14"></span><h2>9.7. 补充<a class="headerlink" href="#tut-odds" title="Permalink to this headline">¶</a></h2>
<p>有时类似于 Pascal 中“记录（record）”或 C 中“结构（struct）”的数据类型很有用，它将一组已命名的数据项绑定在一起。一个空的类定义可以很好的实现它:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Employee</span><span class="p">:</span>
    <span class="k">pass</span>

<span class="n">john</span> <span class="o">=</span> <span class="n">Employee</span><span class="p">()</span> <span class="c"># Create an empty employee record</span>

<span class="c"># Fill the fields of the record</span>
<span class="n">john</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">&#39;John Doe&#39;</span>
<span class="n">john</span><span class="o">.</span><span class="n">dept</span> <span class="o">=</span> <span class="s">&#39;computer lab&#39;</span>
<span class="n">john</span><span class="o">.</span><span class="n">salary</span> <span class="o">=</span> <span class="mi">1000</span>
</pre></div>
</div>
<p>某一段 Python 代码需要一个特殊的抽象数据结构的话，通常可以传入一个类，事实上这模仿了该类的方法。例如，如果你有一个用于从文件对象中格式化数据的函数，你可以定义一个带有 <tt class="xref py py-meth docutils literal"><span class="pre">read()</span></tt> 和 <tt class="xref py py-meth docutils literal"><span class="pre">readline()</span></tt> 方法的类，以此从字符串缓冲读取数据，然后将该类的对象作为参数传入前述的函数。</p>
<p>实例方法对象也有属性：<tt class="docutils literal"><span class="pre">m.im_self</span></tt> 是一个实例方法所属的对象，而 <tt class="docutils literal"><span class="pre">m.im_func</span></tt> 是这个方法对应的函数对象。</p>
</div>
<div class="section" id="tut-exceptionclasses">
<span id="id15"></span><h2>9.8. 异常也是类<a class="headerlink" href="#tut-exceptionclasses" title="Permalink to this headline">¶</a></h2>
<p>用户自定义异常也可以是类。利用这个机制可以创建可扩展的异常体系。</p>
<p>以下是两种新的，有效的（语义上的）异常抛出形式，使用 <tt class="xref std std-keyword docutils literal"><span class="pre">raise</span></tt> 语句:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">raise</span> <span class="n">Class</span>

<span class="k">raise</span> <span class="n">Instance</span>
</pre></div>
</div>
<p>第一种形式中， <tt class="docutils literal"><span class="pre">instance</span></tt> 必须是 <tt class="xref py py-class docutils literal"><span class="pre">Class</span></tt> 或其派生类的一个实例。第二种形式是以下形式的简写:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">raise</span> <span class="n">Class</span><span class="p">()</span>
</pre></div>
</div>
<p>发生的异常其类型如果是 <tt class="xref std std-keyword docutils literal"><span class="pre">except</span></tt> 子句中列出的类，或者是其派生类，那么它们就是相符的（反过来说－－发生的异常其类型如果是异常子句中列出的类的基类，它们就不相符）。例如，以下代码会按顺序打印 B，C，D:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">B</span><span class="p">(</span><span class="ne">Exception</span><span class="p">):</span>
    <span class="k">pass</span>
<span class="k">class</span> <span class="nc">C</span><span class="p">(</span><span class="n">B</span><span class="p">):</span>
    <span class="k">pass</span>
<span class="k">class</span> <span class="nc">D</span><span class="p">(</span><span class="n">C</span><span class="p">):</span>
    <span class="k">pass</span>

<span class="k">for</span> <span class="n">cls</span> <span class="ow">in</span> <span class="p">[</span><span class="n">B</span><span class="p">,</span> <span class="n">C</span><span class="p">,</span> <span class="n">D</span><span class="p">]:</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">raise</span> <span class="n">cls</span><span class="p">()</span>
    <span class="k">except</span> <span class="n">D</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">&quot;D&quot;</span><span class="p">)</span>
    <span class="k">except</span> <span class="n">C</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">&quot;C&quot;</span><span class="p">)</span>
    <span class="k">except</span> <span class="n">B</span><span class="p">:</span>
        <span class="k">print</span><span class="p">(</span><span class="s">&quot;B&quot;</span><span class="p">)</span>
</pre></div>
</div>
<p>要注意的是如果异常子句的顺序颠倒过来（ <tt class="docutils literal"><span class="pre">execpt</span> <span class="pre">B</span></tt> 在最前），它就会打印 B，B，B－－第一个匹配的异常被触发。</p>
<p>打印一个异常类的错误信息时，先打印类名，然后是一个空格、一个冒号，然后是用内置函数 <tt class="xref py py-func docutils literal"><span class="pre">str()</span></tt> 将类转换得到的完整字符串。</p>
</div>
<div class="section" id="tut-iterators">
<span id="id16"></span><h2>9.9. 迭代器<a class="headerlink" href="#tut-iterators" title="Permalink to this headline">¶</a></h2>
<p>现在你可能注意到大多数容器对象都可以用 <tt class="xref std std-keyword docutils literal"><span class="pre">for</span></tt> 遍历:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">]:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="n">element</span><span class="p">)</span>
<span class="k">for</span> <span class="n">key</span> <span class="ow">in</span> <span class="p">{</span><span class="s">&#39;one&#39;</span><span class="p">:</span><span class="mi">1</span><span class="p">,</span> <span class="s">&#39;two&#39;</span><span class="p">:</span><span class="mi">2</span><span class="p">}:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">key</span><span class="p">)</span>
<span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="s">&quot;123&quot;</span><span class="p">:</span>
    <span class="k">print</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="nb">open</span><span class="p">(</span><span class="s">&quot;myfile.txt&quot;</span><span class="p">):</span>
    <span class="k">print</span><span class="p">(</span><span class="n">line</span><span class="p">)</span>
</pre></div>
</div>
<p>这种形式的访问清晰、简洁、方便。迭代器的用法在 Python 中普遍而且统一。在后台， <tt class="xref std std-keyword docutils literal"><span class="pre">for</span></tt> 语句在容器对象中调用 <tt class="xref py py-func docutils literal"><span class="pre">iter()</span></tt> 。 该函数返回一个定义了 <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> 方法的迭代器对象，它在容器中逐一访问元素。没有后续的元素时， <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> 抛出一个 <tt class="xref py py-exc docutils literal"><span class="pre">StopIteration</span></tt> 异常通知 <tt class="xref std std-keyword docutils literal"><span class="pre">for</span></tt> 语句循环结束。以下是其工作原理的示例:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">s</span> <span class="o">=</span> <span class="s">&#39;abc&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">it</span> <span class="o">=</span> <span class="nb">iter</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">it</span>
<span class="go">&lt;iterator object at 0x00A1DB50&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="go">&#39;a&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="go">&#39;b&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="go">&#39;c&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="gt">Traceback (most recent call last):</span>
  File <span class="nb">&quot;&lt;stdin&gt;&quot;</span>, line <span class="m">1</span>, in <span class="n">?</span>
    <span class="nb">next</span><span class="p">(</span><span class="n">it</span><span class="p">)</span>
<span class="gr">StopIteration</span>
</pre></div>
</div>
<p>了解了迭代器协议的后台机制，就可以很容易的给自己的类添加迭代器行为。定义一个 <tt class="xref py py-meth docutils literal"><span class="pre">__iter__()</span></tt> 方法，使其返回一个带有 <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> 方法的对象。如果这个类已经定义了 <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> ，那么 <tt class="xref py py-meth docutils literal"><span class="pre">__iter__()</span></tt> 只需要返回 <tt class="docutils literal"><span class="pre">self</span></tt>:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">class</span> <span class="nc">Reverse</span><span class="p">:</span>
    <span class="sd">&quot;&quot;&quot;Iterator for looping over a sequence backwards.&quot;&quot;&quot;</span>
    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">data</span><span class="p">):</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
    <span class="k">def</span> <span class="nf">__iter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">self</span>
    <span class="k">def</span> <span class="nf">__next__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="k">raise</span> <span class="ne">StopIteration</span>
        <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">index</span> <span class="o">-</span> <span class="mi">1</span>
        <span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">data</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">index</span><span class="p">]</span>
</pre></div>
</div>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">rev</span> <span class="o">=</span> <span class="n">Reverse</span><span class="p">(</span><span class="s">&#39;spam&#39;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">iter</span><span class="p">(</span><span class="n">rev</span><span class="p">)</span>
<span class="go">&lt;__main__.Reverse object at 0x00A1DB50&gt;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">rev</span><span class="p">:</span>
<span class="gp">... </span>    <span class="k">print</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="gp">...</span>
<span class="go">m</span>
<span class="go">a</span>
<span class="go">p</span>
<span class="go">s</span>
</pre></div>
</div>
</div>
<div class="section" id="tut-generators">
<span id="id17"></span><h2>9.10. 生成器<a class="headerlink" href="#tut-generators" title="Permalink to this headline">¶</a></h2>
<p><em class="xref std std-term">Generator</em> 是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数，需要返回数据的时候使用 <tt class="xref std std-keyword docutils literal"><span class="pre">yield</span></tt> 语句。每次 <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> 被调用时，生成器回复它脱离的位置（它记忆语句最后一次执行的位置和所有的数据值）。以下示例演示了生成器可以很简单的创建出来:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="k">def</span> <span class="nf">reverse</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
    <span class="k">for</span> <span class="n">index</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">):</span>
        <span class="k">yield</span> <span class="n">data</span><span class="p">[</span><span class="n">index</span><span class="p">]</span>
</pre></div>
</div>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="k">for</span> <span class="n">char</span> <span class="ow">in</span> <span class="n">reverse</span><span class="p">(</span><span class="s">&#39;golf&#39;</span><span class="p">):</span>
<span class="gp">... </span>    <span class="k">print</span><span class="p">(</span><span class="n">char</span><span class="p">)</span>
<span class="gp">...</span>
<span class="go">f</span>
<span class="go">l</span>
<span class="go">o</span>
<span class="go">g</span>
</pre></div>
</div>
<p>前一节中描述了基于类的迭代器，它能作的每一件事生成器也能作到。因为自动创建了 <tt class="xref py py-meth docutils literal"><span class="pre">__iter__()</span></tt> 和 <tt class="xref py py-meth docutils literal"><span class="pre">next()</span></tt> 方法，生成器显得如此简洁。</p>
<p>另一个关键的功能在于两次执行之间，局部变量和执行状态都自动的保存下来。这使函数很容易写，而且比使用 <tt class="docutils literal"><span class="pre">self.index</span></tt> 和 <tt class="docutils literal"><span class="pre">self.data</span></tt> 之类的方式更清晰。</p>
<p>除了创建和保存程序状态的自动方法，当发生器终结时，还会自动抛出 <tt class="xref py py-exc docutils literal"><span class="pre">StopIteration</span></tt>  异常。综上所述，这些功能使得编写一个正规函数成为创建迭代器的最简单方法。</p>
</div>
<div class="section" id="tut-genexps">
<span id="id18"></span><h2>9.11. 生成器表达式<a class="headerlink" href="#tut-genexps" title="Permalink to this headline">¶</a></h2>
<p>有时简单的生成器可以用简洁的方式调用，就像不带中括号的链表推导式。这些表达式是为函数调用生成器而设计的。生成器表达式比完整的生成器定义更简洁，但是没有那么多变，而且通常比等价的链表推导式更容易记。</p>
<p>例如:</p>
<div class="highlight-python"><div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="nb">sum</span><span class="p">(</span><span class="n">i</span><span class="o">*</span><span class="n">i</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">10</span><span class="p">))</span>                 <span class="c"># sum of squares</span>
<span class="go">285</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">xvec</span> <span class="o">=</span> <span class="p">[</span><span class="mi">10</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">30</span><span class="p">]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">yvec</span> <span class="o">=</span> <span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">sum</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">y</span> <span class="k">for</span> <span class="n">x</span><span class="p">,</span><span class="n">y</span> <span class="ow">in</span> <span class="nb">zip</span><span class="p">(</span><span class="n">xvec</span><span class="p">,</span> <span class="n">yvec</span><span class="p">))</span>         <span class="c"># dot product</span>
<span class="go">260</span>

<span class="gp">&gt;&gt;&gt; </span><span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">pi</span><span class="p">,</span> <span class="n">sin</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">sine_table</span> <span class="o">=</span> <span class="p">{</span><span class="n">x</span><span class="p">:</span> <span class="n">sin</span><span class="p">(</span><span class="n">x</span><span class="o">*</span><span class="n">pi</span><span class="o">/</span><span class="mi">180</span><span class="p">)</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">91</span><span class="p">)}</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">unique_words</span> <span class="o">=</span> <span class="nb">set</span><span class="p">(</span><span class="n">word</span>  <span class="k">for</span> <span class="n">line</span> <span class="ow">in</span> <span class="n">page</span>  <span class="k">for</span> <span class="n">word</span> <span class="ow">in</span> <span class="n">line</span><span class="o">.</span><span class="n">split</span><span class="p">())</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">valedictorian</span> <span class="o">=</span> <span class="nb">max</span><span class="p">((</span><span class="n">student</span><span class="o">.</span><span class="n">gpa</span><span class="p">,</span> <span class="n">student</span><span class="o">.</span><span class="n">name</span><span class="p">)</span> <span class="k">for</span> <span class="n">student</span> <span class="ow">in</span> <span class="n">graduates</span><span class="p">)</span>

<span class="gp">&gt;&gt;&gt; </span><span class="n">data</span> <span class="o">=</span> <span class="s">&#39;golf&#39;</span>
<span class="gp">&gt;&gt;&gt; </span><span class="nb">list</span><span class="p">(</span><span class="n">data</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span><span class="p">))</span>
<span class="go">[&#39;f&#39;, &#39;l&#39;, &#39;o&#39;, &#39;g&#39;]</span>
</pre></div>
</div>
<p class="rubric">Footnotes</p>
<table class="docutils footnote" frame="void" id="id19" rules="none">
<colgroup><col class="label" /><col /></colgroup>
<tbody valign="top">
<tr><td class="label"><a class="fn-backref" href="#id3">[1]</a></td><td>有一个例外。模块对象有一个隐秘的只读对象，名为 <tt class="xref py py-attr docutils literal"><span class="pre">__dict__</span></tt> ，它返回用于实现模块命名空间的字典，命名 <tt class="xref py py-attr docutils literal"><span class="pre">__dict__</span></tt>  是一个属性而非全局命名。显然，使用它违反了命名空间实现的抽象原则，应该被严格限制于调试中。</td></tr>
</tbody>
</table>
</div>
</div>


          </div>
        </div>
      </div>
      <div class="sphinxsidebar">
        <div class="sphinxsidebarwrapper">
  <h3><a href="index.html">Table Of Contents</a></h3>
  <ul>
<li><a class="reference internal" href="#">9. 类</a><ul>
<li><a class="reference internal" href="#tut-object">9.1. 术语相关</a></li>
<li><a class="reference internal" href="#python">9.2. Python 作用域和命名空间</a><ul>
<li><a class="reference internal" href="#tut-scopeexample">9.2.1. 作用域和命名空间示例</a></li>
</ul>
</li>
<li><a class="reference internal" href="#tut-firstclasses">9.3. 初识类</a><ul>
<li><a class="reference internal" href="#tut-classdefinition">9.3.1. 类定义语法</a></li>
<li><a class="reference internal" href="#tut-classobjects">9.3.2. 类对象</a></li>
<li><a class="reference internal" href="#tut-instanceobjects">9.3.3. 实例对象</a></li>
<li><a class="reference internal" href="#tut-methodobjects">9.3.4. 方法对象</a></li>
</ul>
</li>
<li><a class="reference internal" href="#tut-remarks">9.4. 一些说明</a></li>
<li><a class="reference internal" href="#tut-inheritance">9.5. 继承</a><ul>
<li><a class="reference internal" href="#tut-multiple">9.5.1. 多继承</a></li>
</ul>
</li>
<li><a class="reference internal" href="#tut-private">9.6. 私有变量</a></li>
<li><a class="reference internal" href="#tut-odds">9.7. 补充</a></li>
<li><a class="reference internal" href="#tut-exceptionclasses">9.8. 异常也是类</a></li>
<li><a class="reference internal" href="#tut-iterators">9.9. 迭代器</a></li>
<li><a class="reference internal" href="#tut-generators">9.10. 生成器</a></li>
<li><a class="reference internal" href="#tut-genexps">9.11. 生成器表达式</a></li>
</ul>
</li>
</ul>

  <h4>Previous topic</h4>
  <p class="topless"><a href="errors.html"
                        title="previous chapter">8. 错误和异常</a></p>
  <h4>Next topic</h4>
  <p class="topless"><a href="stdlib.html"
                        title="next chapter">10. Python 标准库概览</a></p>
  <h3>This Page</h3>
  <ul class="this-page-menu">
    <li><a href="_sources/classes.txt"
           rel="nofollow">Show Source</a></li>
  </ul>
<div id="searchbox" style="display: none">
  <h3>Quick search</h3>
    <form class="search" action="search.html" method="get">
      <input type="text" name="q" />
      <input type="submit" value="Go" />
      <input type="hidden" name="check_keywords" value="yes" />
      <input type="hidden" name="area" value="default" />
    </form>
    <p class="searchtip" style="font-size: 90%">
    Enter search terms or a module, class or function name.
    </p>
</div>
<script type="text/javascript">$('#searchbox').show(0);</script>
        </div>
      </div>
      <div class="clearer"></div>
    </div>
    <div class="related">
      <h3>Navigation</h3>
      <ul>
        <li class="right" style="margin-right: 10px">
          <a href="genindex.html" title="General Index"
             >index</a></li>
        <li class="right" >
          <a href="stdlib.html" title="10. Python 标准库概览"
             >next</a> |</li>
        <li class="right" >
          <a href="errors.html" title="8. 错误和异常"
             >previous</a> |</li>
        <li><a href="index.html">Python tutorial 3.4 documentation</a> &raquo;</li> 
      </ul>
    </div>
    <div class="footer">
        &copy; Copyright 2013, D.D.
      Created using <a href="http://sphinx-doc.org/">Sphinx</a> 1.2.1.
    </div>
  </body>
</html>